#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sstream>
#include <iostream>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include "logger.h"
#include "clogger.h"

inline const char *shortname(const char *filename)
{ 
    return strrchr(filename, '/') != 0 ? strrchr(filename, '/')+1 : filename;
}

using namespace std;
const char *LogLevelName[8] =
{
    "EMERGENCY",
    "ALERT",
    "CRITICAL",
    "ERROR",
    "WARNING",
    "NOTICE",
    "INFO",
    "DEBUG"
} ;
Logger *Logger::InstanceM = NULL;

Logger::Logger():
mode_(LOG_REAL_TIME),
CurrentLogLine(0),
MaxLogLine(300000),
ComponentNameM("bes"),
LogFileNameM("/mnt/usr/log/bes.log"),
InitializedM(false),
ShouldLogToScreen(false),
ShouldLogToFile(true),
RollOverLogM(false),
pidM((uint32_t) getpid()),
msgSendPtr(NULL)
{
    memset(CountLog, 0, sizeof(CountLog));
    memset(OriginalLogMessage, 0, LogBufferLengthM);
    pthread_mutex_init(&counter_mutex, NULL);
};


bool Logger::initialize(std::string comName, std::string path,  LOG_MODE mode, MSGSENDTYPE func)
{
    ComponentNameM = comName;
    LogFileNameM = path;
    mode_ = mode;

    dbgprintf(DEBUG_ALWAYS, stdout, "LOG: componentName[[%s], path[%s], mode[%d]\n", ComponentNameM.c_str(), LogFileNameM.c_str(), mode_);
    
    if (ShouldLogToFile)
    {
        int32_t statRc;
        struct stat logStat;
        do
        {
            errno = 0;
            statRc = stat(LogFileNameM.c_str(), &logStat);
        } while ( (statRc == -1) && (errno == EINTR) );
        CurrentLogLine = 0;
        if ( statRc != -1 )
        {
            CurrentLogLine = logStat.st_size/100;
            RollOverLog();
        }
        else
        {
            LogStream.open(LogFileNameM.c_str(), ios::app);
        }
    }
    InitializedM = true;

    if( mode_==LOG_MESSAGE )
    {
        dbgprintf(DEBUG_ALWAYS, stdout, "%s: BES in log server mode\n", __func__);
        msgSendPtr = func;
    }
    else
    {
        dbgprintf(DEBUG_ALWAYS, stdout, "%s: BES not in log server mode\n", __func__);
    }

    
    return true;
    
}

bool Logger::unintialize()
{
    fflush(stdout);
    fflush(stderr);
    LogStream.close();
    return true;
}

bool Logger::SetLogLevel(int32_t log_Level)
{
    LogLevel = log_Level;
    return true;
}

bool Logger::RollOverLog()
{
    LogStream << "rolling over log ..." << endl << flush;
    CurrentLogLine = 0;
    LogStream.close();
    
    string LastLogFileName(LogFileNameM);
    LastLogFileName += ".last";
    
    remove(LastLogFileName.c_str());
    rename(LogFileNameM.c_str(), LastLogFileName.c_str());
    
    LogStream.open(LogFileNameM.c_str(), ios::app);
    while((dup2(STDOUT_FILENO, STDERR_FILENO) == -1) && (errno== EINTR))
    {
    };
    return true;
   
}

void Logger::LogToStdout(const char* filename, uint32_t lineNumber, int16_t moduleId, const int32_t logLevel, const char *const originalLogMessage)
{
    const int32_t timeBufferSize = 64;
    char timeBuffer[timeBufferSize];
    
    time_t currentTime;
    time(&currentTime);
    strftime(timeBuffer, timeBufferSize, "%Y/%m/%d %Z %H:%M:%S", localtime(&currentTime));
    cout << timeBuffer;
    cout << " " << ComponentNameM;
    cout << "[" << pidM << "]";
    cout << "[module:" << moduleId << "]";
    cout << " " << LogLevelName[logLevel] << " ";
    cout << MessageCouter;
    cout << " " << filename << ":" << lineNumber;
    cout << " " << originalLogMessage;
    cout << endl <<flush; 

}

void Logger::LogToStream(const char* filename, uint32_t lineNumber, int16_t moduleId, const int32_t logLevel, const char *const originalLogMessage)
{
    const int32_t timeBufferSize = 64;
    char timeBuffer[timeBufferSize];
    
    time_t currentTime;
    time(&currentTime);
    strftime(timeBuffer, timeBufferSize, "%Y/%m/%d %Z %H:%M:%S", localtime(&currentTime));
    
    if( mode_==LOG_MESSAGE )
    {
        stringstream logString;
        logString << timeBuffer;
        logString << " " << ComponentNameM;
        logString << "[" << pidM << "]";
        logString << " " << LogLevelName[logLevel] << " ";
        logString << MessageCouter;
        logString << " " << filename << ":" << lineNumber;
        logString << " " << originalLogMessage;
        logString << endl; 
    
        MSGHEADER msgHdr;
        memset(&msgHdr, 0, sizeof(msgHdr));
        msgHdr.dstID = MODULE_LOG_SERVER;
        msgHdr.srcID = moduleId;
        msgHdr.msgType = MSG_TYPE_INTERNAL;
        msgHdr.msgLength = logString.str().length() + sizeof(MSGHEADER);

        if( msgSendPtr!=NULL )
        {
            if( RC_OK!=msgSendPtr(MODULE_LOG_SERVER, &msgHdr, const_cast<char*>(logString.str().c_str())) )
            {
                dbgprintf(DEBUG_ERROR, stderr, "Fail to send MSG. thread id is %lu \n", pthread_self()); 
            }
        }
        else
        {
            dbgprintf(DEBUG_ERROR, stderr, "%s: MSGSEND ptr is NULL\n", __func__); 
        }
        return;
    }
    
    if ( CurrentLogLine >= MaxLogLine)
    {
        RollOverLog();
    }
    else
    {
        CurrentLogLine++;
    }
    
    ostringstream logString;
    logString << timeBuffer;
    logString << " " << ComponentNameM;
    logString << "[" << pidM << "]";
    logString << "[module:" << moduleId << "]";
    logString << " " << LogLevelName[logLevel] << " ";
    logString << MessageCouter;
    logString << " " << filename << ":" << lineNumber;
    logString << " " << originalLogMessage;
    logString << endl; 
    LogStream << logString.str() << flush;
}

void Logger::ServerLogToStream(std::string logContent)
{
    if ( CurrentLogLine >= MaxLogLine)
    {
        RollOverLog();
    }
    else
    {
        CurrentLogLine++;
    }

    LogStream << logContent << flush;
}

void Logger::LogMessage(const char* filename, uint32_t lineNumber, int16_t moduleId, const int32_t logLevel, const char *const originalLogMessage)
{

    if ( ShouldLogToFile )
    {
        LogToStream(filename,lineNumber, moduleId, logLevel, originalLogMessage);

    }
    
    if ( ShouldLogToScreen )
    {
        LogToStdout(filename,lineNumber, moduleId, logLevel, originalLogMessage);
    }
    MessageCouter++;

}

Logger::LOG_MODE Logger::getMode()
{
    return mode_;
}

void Logger::LogMessage(const char* filename, uint32_t lineNumber, int16_t moduleId, const int32_t logLevel, const char* msgFormat, va_list args)
{
    char msgBuf[LogBufferLengthM];
    if( getMode()==Logger::LOG_REAL_TIME )
    {
        vsnprintf(OriginalLogMessage, LogBufferLengthM, msgFormat, args);
    }
    else
    {
        memset(msgBuf, 0, sizeof(msgBuf));
        vsnprintf(msgBuf, LogBufferLengthM, msgFormat, args);
    }
    ++CountLog[logLevel];
    const char* msgPtr = OriginalLogMessage;
    if( getMode()==Logger::LOG_MESSAGE )
    {
        msgPtr = msgBuf;
    }
    if (!InitializedM)
    {
        LogToStdout(filename, lineNumber, moduleId, logLevel, msgPtr);
        return;
    }
    LogMessage(filename, lineNumber, moduleId, logLevel, msgPtr);
}

void CLogger(int moduleId, const char *filename, uint32_t lineNumber, int32_t logLevel, const char* const msgFormat, ...)
{
    if (LoggerPtr->logMessageFilter() < logLevel)
    {
        return;
    }

    if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME )
    {
        LoggerPtr->lock();
    }

    va_list ap;
    va_start(ap, msgFormat);
    LoggerPtr->LogMessage(shortname(filename), lineNumber, moduleId, logLevel, msgFormat, ap);
    
    if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME )
    {
        LoggerPtr->unlock();
    }
}

int CSetLogLevel(int32_t logLevel)
{
    if(logLevel <= LOG_USE_GLOBAL_FILTER)
    {
        LoggerPtr->SetLogLevel(logLevel);
        return 0;
    }
    else
    {
        return 1;
    }   

}

